package com.bestcem.xm.componet.tinyurl.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bestcem.xm.componet.tinyurl.entity.TinyurlMapping;
import com.bestcem.xm.componet.tinyurl.mapper.TinyurlMappingMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

@Slf4j
public class TinyurlService implements InitializingBean {

    private final static String SHORT_URL_INCREMENT_HASH_KEY = "shortUrl:incrementHashKey"; //自增hash key
    private final static String ID_KEY = "idkey"; //自增主键
    //短链key
    private final static String SHORT_URL_KEY = "shortUrl:%s";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private TinyurlMappingMapper tinyurlMappingMapper;

    @Value("${app.tinyurl.domain:http://cloud-dev.bestcem.cn/t/}")
    private String shortUrlDomain;

    @Override
    public void afterPropertiesSet() {
        if (Boolean.FALSE.equals(this.redisTemplate.hasKey(SHORT_URL_INCREMENT_HASH_KEY))) {
            TinyurlMapping tinyurlMapping = tinyurlMappingMapper.selectOne(new LambdaQueryWrapper<TinyurlMapping>()
                    .orderByDesc(TinyurlMapping::getShortUrlKeyidLong).last("limit 1"));
            long maxKeyidLong;
            if (ObjectUtils.isEmpty(tinyurlMapping) || tinyurlMapping.getShortUrlKeyidLong() == null) {
                maxKeyidLong = 916132833L;
            } else {
                maxKeyidLong = tinyurlMapping.getShortUrlKeyidLong();
            }
            //Math.pow(62, 5) + 1 = 916132833 自动转科学计数，导致出错
            this.redisTemplate.opsForHash().putIfAbsent(SHORT_URL_INCREMENT_HASH_KEY, ID_KEY, maxKeyidLong);
        }
    }

    /**
     * 下一个随机自增序列
     */
    private Long next() {
        return this.redisTemplate.opsForHash().increment(SHORT_URL_INCREMENT_HASH_KEY, ID_KEY, 1);
    }

    private static final char[] charSet = "R5VAarqYi7sGFZogdw9zemj1b6D4OcEL3kWhXt0QfnJvpPCNISyMBUu2lxH8TK".toCharArray(); //乱序（混淆）

    private String convert10to62(long number, int length) {
        long rest = number;
        Stack<Character> stack = new Stack<>();
        StringBuilder result = new StringBuilder(0);
        while (rest != 0) {
            stack.add(charSet[new Long((rest - (rest / 62) * 62)).intValue()]);
            rest = rest / 62;
        }
        while (!stack.isEmpty()) {
            result.append(stack.pop());
        }
        int result_length = result.length();
        StringBuilder temp0 = new StringBuilder();
        for (int i = 0; i < length - result_length; i++) {
            temp0.append('0');
        }

        return temp0 + result.toString();
    }

    /**
     * 生成短链接
     */
    public Map<String, Object> genShortUrl(String orgUrl) {
        Map<String, Object> result = new HashMap<>();
        result.put("long_url", orgUrl);
        result.put("code", 0);

        //判断数据库中是否已经有了
        TinyurlMapping shortUrlMapping = tinyurlMappingMapper.selectOne(
                new LambdaQueryWrapper<TinyurlMapping>().eq(TinyurlMapping::getOriginalUrl, orgUrl));
        if (shortUrlMapping != null) {
            String keyid = shortUrlMapping.getShortUrlKeyid();
            String shortUrl = String.format("%s%s", shortUrlDomain, keyid);

            //放入缓存,防止缓存穿透
            this.redisTemplate.opsForValue().set(String.format(SHORT_URL_KEY, keyid), orgUrl);
            result.put("tcode", keyid);
            result.put("short_url", shortUrl);
            return result;
        }
        //生成自增随机序列
        Long entityId = next();
        //生成对应的keyid
        String keyid = convert10to62(entityId, 6);

        //插入数据库
        shortUrlMapping = new TinyurlMapping();
        shortUrlMapping.setShortUrlKeyid(keyid);
        shortUrlMapping.setShortUrlKeyidLong(entityId);
        shortUrlMapping.setOriginalUrl(orgUrl);
        shortUrlMapping.setCreatedDt(LocalDateTime.now());
        tinyurlMappingMapper.insert(shortUrlMapping);

        //组装成完整的短链
        String shortUrl = String.format("%s%s", shortUrlDomain, keyid);

        //放入缓存,防止缓存穿透
        this.redisTemplate.opsForValue().set(String.format(SHORT_URL_KEY, keyid), orgUrl);

        result.put("tcode", keyid);
        result.put("short_url", shortUrl);
        return result;
    }

    public String toRealUrl(String keyid) {
        String longUrl = (String) redisTemplate.opsForValue().get(String.format(SHORT_URL_KEY, keyid));
        if (ObjectUtils.isEmpty(longUrl)) {
            TinyurlMapping shortUrlMapping = tinyurlMappingMapper.selectOne(
                    new LambdaQueryWrapper<TinyurlMapping>().eq(TinyurlMapping::getShortUrlKeyid, keyid));
            if (ObjectUtils.isEmpty(shortUrlMapping)) {
                return "";
            }
            this.redisTemplate.opsForValue().set(String.format(SHORT_URL_KEY, keyid), shortUrlMapping.getOriginalUrl());
            return shortUrlMapping.getOriginalUrl();
        }
        return longUrl;

    }
}
